# 1. 深拷贝与浅拷贝
- 下面分别测试Object.assign(),lodash中cloneDeep,slice,concat,[...],JSON.parse(JSON.stringify),js方法实现拷贝。
/* 构建对象函数 */
function createObj(obj,prototype) {
Object.setPrototypeOf(obj,prototype)
return obj;
}
1
2
3
4
5
2
3
4
5
# 1. Object.assign()
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
//Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象
//遇到相同的属性随后的下一个对象的属性会覆盖上一个对象的属性,属性值是对象或数组会引用(直接替换)。
console.log(Object.assign({},arr));//{0: 1, 1: 2, 2: {b:{c:'c'}}} __proto__原型上没拷贝来
console.log(Object.assign([],obj));//[a: "a", b: {…}]:length: 0
console.log(Object.assign(obj,arr))//{0: 1, 1: 2, 2: {b:{c:'c'}}, a: "a", b: {c:'c'}} __proto__:{x: "x",y: {z: "z"}}
console.log(Object.assign(arr,obj))//[1, 2, {b:{c:'c'}}, a: "a", b: {c:'c'}]:length:3 __proto__:[3,4,{x:'x',y:{z:'z'}}]
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
copy1=Object.assign([],arr);//[1, 2, {b:{c:'c'}}] __proto__原型上属性不可枚举
copy1[0]=11;
copy1[2].a="aa"
copy1[2].b.c="cc"
console.log(arr,copy1)//arr上原型不变
//[1, 2, {a:'aa',b:{c:'cc'}} ,[11, 2, {a:'aa',b:{c:'cc'}}]
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
copy2=Object.assign({},obj);
copy2.a='aa';
copy2.b.c='cc';
console.log(obj,copy2)//obj上原型不变
//{a:'a',b:{c:'cc'}},{a:'aa',b:{c:'cc'}}
var s=[1,2,3];
var d=[2,3,4];
var s1=[2,3,[1,2,3],[5,33]];
var d1=[1,2,[3,2,1],4];
console.log(Object.assign(s,d))//[2,3,4]
console.log(Object.assign(s1,d1))//[1,2,[3,2,1],4]
// 总结:此方法为浅拷贝,相同的属性名(数组的话index相同),后面会覆盖前面
// lodash中的merge是合并,数组和普通对象会递归合并(数组属性名是下标),值undefined不会覆盖前其他对象和值会被直接分配覆盖。源对象从从左到右分配。后续的来源对象属性会覆盖之前分配的属性。
var object={a:123,g:undefined,t:123,c:[3,5,{e:1,f:4,d:[1],s:{a:2,b:2}},{a:1},7]}
var object2={a:undefined,g:123,t:null,b:2,c:[3,4,{e:3,r:4,d:[2],s:{a:1,e:4}},6]}
console.log(_.merge(object, object2));//{a:123,g:123,t:null,b:2,c:[3,4,{e:3,f:4,r:4,d:[2s:{a:1,b:2,e:4},6,7}]}
//object和object2中的c是数组递归,然后下标0和1位置都是数字,就直接覆盖。位置3处object是对object2处是6,直接覆盖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 2. lodash中cloneDeep
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
copy3=_.cloneDeep(arr)//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy3[0]='11'
copy3[2].a="aa"
copy3[2].b.c="cc"
console.log(arr,copy3)
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
copy4=_.cloneDeep(obj)//{a:'a',b:{c:'c'}} 没有拷贝原型
copy4.a='aa'
copy4.b.c="cc"
console.log(obj,copy4)
//{a:'a',b:{c:'c'}},{a:'aa',b:{c:'cc'}}
//总结:lodash中cloneDeep为深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3. slice
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
//copy5=obj.slice()//obj.slice is not a function
copy6=arr.slice()//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy6[0]='11'
copy6[2].a="aa"
copy6[2].b.c="cc"
console.log(arr,copy6)//arr上原型不变
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
//总结:和Object.assign作用一样,只不过Object.assign对象和数组都可以用,slice只能用于数组
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 4. concat
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
// copy7=obj.concat()//obj.concat is not a function
copy8=arr.concat()//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy8[0]='11'
copy8[2].a="aa"
copy8[2].b.c="cc"
console.log(arr,copy8)//arr上原型不变
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
//总结:和Object.assign与slice作用一样,只不过Object.assign对象和数组都可以用,cancat只能用于数组
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 5. 扩展运算符
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
copy9=[...arr]//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy9[0]='11'
copy9[2].a="aa"
copy9[2].b.c="cc"
console.log(arr,copy9)//arr上原型不变
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
//copy10=[...obj]//object is not iterable
copy10={...obj}//{a:'a',b:{c:'c'}}
copy10.a='aa'
copy10.b.c="cc"
console.log(obj,copy10)//obj上原型不变
//{a:'a',b:{c:'cc'}},{a:'aa',b:{c:'cc'}}
//总结:...和Object.assign,slice,concat作用是一样的,对象数组都可以用,二级对象及以后为浅拷贝,一级对象为深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 6. JSON.parse(JSON.stringify())
//虽然可行,但不推荐这种用法,可查看(https://www.jianshu.com/p/b084dfaad501)
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
copy11=JSON.parse(JSON.stringify(arr))//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy11[0]='11'
copy11[2].a="aa"
copy11[2].b.c="cc"
console.log(arr,copy11)//arr上原型不变
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
copy12=JSON.parse(JSON.stringify(obj))//{a:'a',b:{c:'c'} 没有拷贝原型
copy12.a='aa'
copy12.b.c="cc"
console.log(obj,copy12)//obj上原型不变
//{a:'a',b:{c:'c'}},{a:'aa',b:{c:'cc'}}
// 总结JSON.parse(JSON.stringify())为深拷贝,但不推荐,
// 碰见时间对象结果为字符串形式
// 碰见RegExp、Error对象序列化后为空,
// 碰见函数,undefined,序列化的结果会把函数或 undefined丢失
// 碰见NaN、Infinity和-Infinity,则序列化的结果会变成null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 7. js方法
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
function clone (value) {
if (Array.isArray(value)) {
return value.map(clone)
} else if (value && typeof value === 'object') {
const res = {}
for (const key in value) {
res[key] = clone(value[key])
}
return res
} else {
return value
}
}
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
copy13=deepClone1(arr)//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy13[0]='11'
copy13[2].a="aa"
copy13[2].b.c="cc"
console.log(arr,copy13)
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
copy14=deepClone1(obj)//{a:'a',b:{c:'c'} 没有拷贝原型
copy14.a='aa'
copy14.b.c="cc"
console.log(obj,copy14)
//{a:'a',b:{c:'c'}},{a:'aa',b:{c:'cc'}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 8. 自身属性和原型上的浅拷贝
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
function moreClone(origin) {
let oriProto = Object.getPrototypeOf(origin);
return Object.assign(Object.create(oriProto), origin);
}
copy15=moreClone(arr);//[1,2,{a:'a',b:{c:'c'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}]
copy15[0]='11'
copy15[2].a="aa"
copy15[2].b.c="cc"
copy15.__proto__[0]=33
copy15.__proto__[2].x='xx'
copy15.__proto__[2].y.z='zz'
console.log(arr,copy15)
// [1,2,{a:'aa',b:{c:'cc'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}],
// [11,2,{a:'aa',b:{c:'cc'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}]
copy16=moreClone(obj);//{a:'a',b:{c:'c'}} __proto__:{x: "x",y: {z: "z"}}
copy16.a='aa'
copy16.b.c='cc'
copy16.__proto__.x='xx'//copy16.x=x是给自身对象加x:x
copy16.__proto__.y.z='zz'//copy16.y.z='zz'是给原型上y.z改成zz
console.log(obj,copy16)
//{a:'a',b:{c:'cc'}} __proto__:{x: "xx",y: {z: "zz"}},
//{a:'aa',b:{c:'cc'}} __proto__:{x: "xx",y: {z: "zz"}}
//总结:自身和原型上都是浅拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 9. 利用7和8实现自身属性浅拷贝和原型上的深拷贝
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
function moreClone1(origin) {
let oriProto = _.cloneDeep(Object.getPrototypeOf(origin));//当然可以用上面的deepClon 代替_.cloneDeep
return Object.assign(Object.create(oriProto), origin);
}
copy17=moreClone1(arr);//[1,2,{a:'a',b:{c:'c'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}]
copy17[0]='11'
copy17[2].a="aa"
copy17[2].b.c="cc"
copy17.__proto__[0]=33
copy17.__proto__[2].x='xx'
copy17.__proto__[2].y.z='zz'
console.log(arr,copy17)
// [1,2,{a:'aa',b:{c:'cc'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}],
// [11,2,{a:'aa',b:{c:'cc'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}
copy18=moreClone1(obj);//{a:'a',b:{c:'c'}} __proto__:{x: "x",y: {z: "z"}}
copy18.a='aa'
copy18.b.c='cc'
copy18.__proto__.x='xx'//copy18.x=x是给自身对象加x:x
copy18.__proto__.y.z='zz'//copy18.y.z='zz'是给原型上y.z改成zz
console.log(obj,copy18)
//{a:'a',b:{c:'cc'}} __proto__:{x: "x",y: {z: "z"}},
//{a:'aa',b:{c:'cc'}} __proto__:{x: "xx",y: {z: "zz"}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 10. 利用9实现自身深拷贝和原型上的浅拷贝
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
function moreClone2(origin) {
let oriProto = Object.getPrototypeOf(origin);//当然可以用上面的deepClone1代替_.cloneDeep
return Object.assign(Object.create(oriProto), _.cloneDeep(origin));
}
copy19=moreClone2(arr);//[1,2,{a:'a',b:{c:'c'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}]
copy19[0]='11'
copy19[2].a="aa"
copy19[2].b.c="cc"
copy19.__proto__[0]=33
copy19.__proto__[2].x='xx'
copy19.__proto__[2].y.z='zz'
console.log(arr,copy19)
// [1,2,{a:'c',b:{c:'c'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}],
// [11,2,{a:'aa',b:{c:'cc'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}]
copy20=moreClone2(obj);//{a:'a',b:{c:'c'}} __proto__:{x: "x",y: {z: "z"}}
copy20.a='aa'
copy20.b.c='cc'
copy20.__proto__.x='xx'//copy20.x=x是给自身对象加x:x
copy20.__proto__.y.z='zz'//copy20.y.z='zz'是给原型上y.z改成zz
console.log(obj,copy20)
//{a:'a',b:{c:'c'}} __proto__:{x: "xx",y: {z: "zz"}},
//{a:'aa',b:{c:'cc'}} __proto__:{x: "xx",y: {z: "zz"}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 11. 利用9实现自身和原型上的深拷贝
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
function moreClone3(origin) {
let oriProto = _.cloneDeep(Object.getPrototypeOf(origin));//当然可以用上面的deepClone1代替_.cloneDeep
return Object.assign(Object.create(oriProto), _.cloneDeep(origin));
}
copy21=moreClone3(arr);//[1,2,{a:'a',b:{c:'c'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}]
copy21[0]='11'
copy21[2].a="aa"
copy21[2].b.c="cc"
copy21.__proto__[0]=33
copy21.__proto__[2].x='xx'
copy21.__proto__[2].y.z='zz'
console.log(arr,copy21)
// [1,2,{a:'c',b:{c:'c'}}] __proto__:[3,4,{x:'x',y:{z:'z'}}],
// [11,2,{a:'aa',b:{c:'cc'}}] __proto__:[33,44,{x:'xx',y:{z:'zz'}}]
copy22=moreClone3(obj);//{a:'a',b:{c:'c'}} __proto__:{x: "x",y: {z: "z"}}
copy22.a='aa'
copy22.b.c='cc'
copy22.__proto__.x='xx'//copy22.x=x是给自身对象加x:x
copy22.__proto__.y.z='zz'//copy22.y.z='zz'是给原型上y.z改成zz
console.log(obj,copy22)
//{a:'a',b:{c:'c'}} __proto__:{x: "x",y: {z: "z"}},
//{a:'aa',b:{c:'cc'}} __proto__:{x: "xx",y: {z: "zz"}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 12. Array.from()
var arr=createObj([1,2,{a:'a',b:{c:'c'}}],[3,4,{x:'x',y:{z:'z'}}])
var obj=createObj({a:'a',b:{c:'c'}},{x:'x',y:{z:'z'}})
//Array.from()是ES6中语法,只能用于js集合(如: 数组、类数组对象(就是键是数组下标,并有length属性,类似{0:1,1:'x',length:2}这样的)、或者是字符串、map 、set 等可迭代对象),所以对于obj是无法使用的。
copy23=Array.from(obj);//[]
copy24=Array.from(arr);//[1,2,{a:'a',b:{c:'c'}}] 没有拷贝原型
copy24[0]='11'
copy24[2].a="aa"
copy24[2].b.c="cc"
console.log(arr,copy24)//arr上原型不变
//[1,2,{a:'a',b:{c:'c'}}],[11,2,{a:'aa',b:{c:'cc'}}]
//也可以使用Array.from()实现js集合的深拷贝,包含对象的只能是浅拷贝
function recursiveClone(val) {
return Array.isArray(val) ? Array.from(val, recursiveClone) : val;
}
var arr=[1,2,[3,4,[5,6]]]
copy25=Array.from(arr, recursiveClone)
copy25[0]=11
copy25[2][1]=33
copy25[2][2][1]=55
console.log(arr,copy25)
//[1,2,[3,4,[5,6]]],[11,2,[32,4,[55,6]]]
//这种是利用递归,把拷贝嵌套的每一级然后返回。你也可以利用上面的方法实现自身和原型上的深拷贝,但是只能针对js集合
//总结:和Object.assign,slice,concat作用是一样的,只有类数组可以用,二级对象及以后为浅拷贝,一级对象为深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 总结
# 深拷贝是指目标对象不会随源对象改变而改变
# 浅拷贝是指目标对象会随着源对象改变而改变
对象中只考虑自身的深拷贝可用:lodash.cloneDeep,JSON.parse(JSON.stringify()),js方法
数组中只考虑自身的深拷贝可用:lodash.cloneDeep,JSON.parse(JSON.stringify()),js方法
对象中只考虑自身的浅拷贝可用:Object.assign(),扩展运算符...
数组中只考虑自身的浅拷贝可用:Object.assign(),扩展运算符...,slice,concat
考虑原型上的复制:js方法